package data

import (
	"context"
	"digitalhuman/internal/conf"
	"digitalhuman/internal/cons"
	"digitalhuman/internal/helper"
	"digitalhuman/models"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/go-kratos/kratos/v2/encoding/json"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/google/wire"
	_ "github.com/lib/pq"
	"github.com/redis/go-redis/v9"
	"github.com/wenqingqaq/backend-lib/rds"
	"go.uber.org/zap"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"os"
	"strings"
	"time"
)

// ProviderSet is data providers.  NewUserData
var ProviderSet = wire.NewSet(NewData, NewGreeterRepo, NewUserBiz, NewOssAliClientBucket, NewAssetData,
	NewXunFei, NewTextAudioData, NewUserData, NewVisualMaterialData, NewDouYinData, NewNatsData, NewDsData,
	NewStoryboardData, NewStoryboardSectionData, NewProduceData,
)

// Data .
type Data struct {
	Rdb          *redis.Client
	Db           *gorm.DB
	Config       *conf.Data
	Nats         *NatsData
	BucketClient *oss.Bucket // oss bucket 连接客户端， 请求大了可以考虑客户端连接池
	// TODO wrapped database client
}

// NewData .
func NewData(c *conf.Data, oss *OssAli, nat *NatsData) (*Data, func(), error) {
	json.MarshalOptions.UseProtoNames = true
	data := &Data{}
	data.Rdb = NewRds(c)
	data.Db = NewGorm(c, data.Rdb, oss)
	data.Config = c
	data.Nats = nat
	cleanup := func() {
		log.Info("closing the data resources")
		if err := data.Rdb.Close(); err != nil {
			log.Error("rds disconnect failed", zap.Error(err))
			return
		}
		log.Info("rds disconnected~")
	}
	return data, cleanup, nil
}

func NewGorm(conf *conf.Data, r *redis.Client, oss *OssAli) *gorm.DB {
	psqlInfo := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s TimeZone=Asia/Shanghai",
		conf.Database.Host,
		conf.Database.Port,
		conf.Database.User,
		conf.Database.Password,
		conf.Database.DbName,
		conf.Database.SslMode)
	db, err := gorm.Open(postgres.New(postgres.Config{
		DSN:                  psqlInfo,
		PreferSimpleProtocol: true, // disables implicit prepared statement usage
	}), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	if err != nil {
		panic(err)
	}

	//sqlDB, err := db.DB()
	//
	//sqlDB.SetMaxIdleConns(10)
	//sqlDB.SetMaxOpenConns(100)
	//sqlDB.SetConnMaxLifetime(time.Hour)

	ts := helper.NanoTimestampStr()
	ctx := context.Background()
	if rds.Lock(ctx, r, cons.LockKeyMysqlSyncModel, ts, time.Minute) {
		defer rds.Unlock(ctx, r, cons.LockKeyMysqlSyncModel, ts)
		err := db.AutoMigrate(&models.User{}, &models.Asset{}, &models.VisualMaterial{},
			&models.TextAudio{}, &models.StoryboardSection{}, &models.Storyboard{})
		if err != nil {
			panic(err)
		}
	}

	// 判断一下数据表资源数据是否已经插入
	var asset *models.Asset
	db.Where("id = 1").First(&asset)
	if asset.ID == 0 { // 初始导入判断
		sqlStr, err := os.ReadFile(conf.Database.InitDbFile) // 导入sql文件 一般是项目根目录下的一个文件
		if err != nil {
			panic(err)
		}
		sqlArr := strings.Split(string(sqlStr), ";")
		for _, sql := range sqlArr {
			if sql == "" {
				continue
			}
			db.Exec(sql)
		}

		// 初始化的时候资源文件导入一下oss
		err = SyncDefaultAsset(oss)
		if err != nil {
			panic(err)
		}
	}
	err = SyncDefaultAsset(oss)
	fmt.Println(err)

	return db
}
